using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Xml;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System.ServiceModel.Description;
using LoadController;


namespace LoadGenerator
{
    public class WorkerRole : RoleEntryPoint
    {
        private ServiceHost serviceHost;
        private string InstanceName = "IN_" + RoleEnvironment.CurrentRoleInstance.Id.Split(new char[] { '_' }).Last();
        private Job CurrentJob;
        private GeneratorConfigurations.GeneratorConfiguration CurrentConfig;
        private InvokeJob invokeJob;
        
        public override bool OnStart()
        {
            Trace.WriteLine("LoadGenerator OnStart()", "Information");
            
            // Legen Sie die maximale Anzahl von gleichzeitigen Verbindungen fest. 
            ServicePointManager.DefaultConnectionLimit = 12;

            //check if tables and blob containers exist
            try
            {
                AppSettings.CreateTablesIfNotExist();
                AppSettings.CreateBlobContainersIfNotExist();
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Can't create tables or blob containers.");
                Trace.WriteLine(ex.ToString());
            }

            serviceHost = RoleCommunicator.GetServiceHost();

            (serviceHost.SingletonInstance as JobService).OnJobAborted  += new EventHandler(serviceHost_OnJobAborted);

            return base.OnStart();
        }




        public override void Run()
        {
            Trace.WriteLine("LoadGenerator entry point called", "Information");
            
            while (true)
            {
                Thread.Sleep(1000);
                Trace.WriteLine("Retrieving Job", "Information");
                
                var activeJob = AzureTableHelper.GetCloudTable(AppSettings.Table_ActiveJob).ExecuteQuery<Job>(new TableQuery<Job>());

                if (activeJob.Count() > 0)
                {
                    var number = RoleEnvironment.CurrentRoleInstance.Id.Split(new char[] { '_' }).Last();
                    var query = activeJob.Where(x => x.PartitionKey.Split(new char[] { '_' }).Last() == number);

                    if (query.Count() == 1)
                    {
                        CurrentJob = query.Single();

                        Initialize();

                        ScheduleJob();
                    }
                }                
            }
        }


        private void Initialize()
        {
            invokeJob = new InvokeJob(CurrentJob);
            
            var gcfs = GeneratorConfigurations.Deserialize(CurrentJob.LoadConfigurations);
            CurrentConfig = gcfs.Where(x => x.InstanceName == InstanceName).Single<GeneratorConfigurations.GeneratorConfiguration>();
        }


        private void serviceHost_OnJobAborted(object sender, EventArgs e)
        {
            RoleEnvironment.RequestRecycle();
        }


        private bool isAborted;
        private AutoResetEvent autoEvent;
        private List<Thread> SampleThreadList;
        

        private void ScheduleJob()
        {
            isAborted = false;
            autoEvent = new AutoResetEvent(false);
            SampleThreadList = new List<Thread>();
                
            Trace.WriteLine("Job started: " + DateTime.UtcNow.ToString("HH:mm:ss:fff"));

            autoEvent.WaitOne(CurrentConfig.StartUpDelay*60*1000);

            for (int i = 0; i < CurrentConfig.NumberOfThreads; i++)
            {
                if (isAborted) break;

                Thread sampleThread = new Thread(InvokeServiceMethod);
                SampleThreadList.Add(sampleThread);
                sampleThread.Start((object)i);
                
                autoEvent.WaitOne(CurrentConfig.ThreadStartUpDelay*1000);
            }


            autoEvent.WaitOne(CurrentConfig.CalculateDuration()*1000);
            isAborted = true;

            foreach (Thread t in SampleThreadList)
            {
                t.Join();
            }


            invokeJob.Uninitialize();

            Trace.WriteLine("Jobs ended:" + DateTime.UtcNow.ToString("HH:mm:ss:fff"));
                                                                           
            AzureTableHelper.GetCloudTable(AppSettings.Table_ActiveJob).Execute(TableOperation.Delete(CurrentJob));
            
            CurrentJob = null;
        }
       
        private void InvokeServiceMethod(object threadID)
        {        
            Trace.WriteLine("thread:" + threadID.ToString() + " started " + DateTime.UtcNow.ToString("HH:mm:ss:fff"));

            while (!isAborted)
            {
                Invoke((object)threadID);
            }
            
            Trace.WriteLine("thread:" + threadID.ToString() + " ended " + DateTime.UtcNow.ToString("HH:mm:ss:fff"));
        }

  
        private void Invoke(object threadID)
        {                       
            Sample ThreadSample = new Sample((int)threadID,InstanceName,SampleThreadList.Count);

            invokeJob.Do(ThreadSample);

            AzureTableHelper.GetCloudTable(AppSettings.Table_Samples).Execute(TableOperation.InsertOrMerge(ThreadSample));
        }
    }

}



